import { BufferAttribute, BufferGeometry, FileLoader, Float32BufferAttribute, Loader, LoaderUtils } from 'three'
import { unzlibSync } from 'fflate'
import { decodeText } from '../_polyfill/LoaderUtils'

class VTKLoader extends Loader {
  constructor(manager) {
    super(manager)
  }

  load(url, onLoad, onProgress, onError) {
    const scope = this

    const loader = new FileLoader(scope.manager)
    loader.setPath(scope.path)
    loader.setResponseType('arraybuffer')
    loader.setRequestHeader(scope.requestHeader)
    loader.setWithCredentials(scope.withCredentials)
    loader.load(
      url,
      function (text) {
        try {
          onLoad(scope.parse(text))
        } catch (e) {
          if (onError) {
            onError(e)
          } else {
            console.error(e)
          }

          scope.manager.itemError(url)
        }
      },
      onProgress,
      onError,
    )
  }

  parse(data) {
    function parseASCII(data) {
      // connectivity of the triangles
      var indices = []

      // triangles vertices
      var positions = []

      // red, green, blue colors in the range 0 to 1
      var colors = []

      // normal vector, one per vertex
      var normals = []

      var result

      // pattern for detecting the end of a number sequence
      var patWord = /^[^\d.\s-]+/

      // pattern for reading vertices, 3 floats or integers
      var pat3Floats = /(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)\s+(\-?\d+\.?[\d\-\+e]*)/g

      // pattern for connectivity, an integer followed by any number of ints
      // the first integer is the number of polygon nodes
      var patConnectivity = /^(\d+)\s+([\s\d]*)/

      // indicates start of vertex data section
      var patPOINTS = /^POINTS /

      // indicates start of polygon connectivity section
      var patPOLYGONS = /^POLYGONS /

      // indicates start of triangle strips section
      var patTRIANGLE_STRIPS = /^TRIANGLE_STRIPS /

      // POINT_DATA number_of_values
      var patPOINT_DATA = /^POINT_DATA[ ]+(\d+)/

      // CELL_DATA number_of_polys
      var patCELL_DATA = /^CELL_DATA[ ]+(\d+)/

      // Start of color section
      var patCOLOR_SCALARS = /^COLOR_SCALARS[ ]+(\w+)[ ]+3/

      // NORMALS Normals float
      var patNORMALS = /^NORMALS[ ]+(\w+)[ ]+(\w+)/

      var inPointsSection = false
      var inPolygonsSection = false
      var inTriangleStripSection = false
      var inPointDataSection = false
      var inCellDataSection = false
      var inColorSection = false
      var inNormalsSection = false

      var lines = data.split('\n')

      for (var i in lines) {
        var line = lines[i].trim()

        if (line.indexOf('DATASET') === 0) {
          var dataset = line.split(' ')[1]

          if (dataset !== 'POLYDATA') throw new Error('Unsupported DATASET type: ' + dataset)
        } else if (inPointsSection) {
          // get the vertices
          while ((result = pat3Floats.exec(line)) !== null) {
            if (patWord.exec(line) !== null) break

            var x = parseFloat(result[1])
            var y = parseFloat(result[2])
            var z = parseFloat(result[3])
            positions.push(x, y, z)
          }
        } else if (inPolygonsSection) {
          if ((result = patConnectivity.exec(line)) !== null) {
            // numVertices i0 i1 i2 ...
            var numVertices = parseInt(result[1])
            var inds = result[2].split(/\s+/)

            if (numVertices >= 3) {
              var i0 = parseInt(inds[0])
              var i1, i2
              var k = 1
              // split the polygon in numVertices - 2 triangles
              for (var j = 0; j < numVertices - 2; ++j) {
                i1 = parseInt(inds[k])
                i2 = parseInt(inds[k + 1])
                indices.push(i0, i1, i2)
                k++
              }
            }
          }
        } else if (inTriangleStripSection) {
          if ((result = patConnectivity.exec(line)) !== null) {
            // numVertices i0 i1 i2 ...
            var numVertices = parseInt(result[1])
            var inds = result[2].split(/\s+/)

            if (numVertices >= 3) {
              var i0, i1, i2
              // split the polygon in numVertices - 2 triangles
              for (var j = 0; j < numVertices - 2; j++) {
                if (j % 2 === 1) {
                  i0 = parseInt(inds[j])
                  i1 = parseInt(inds[j + 2])
                  i2 = parseInt(inds[j + 1])
                  indices.push(i0, i1, i2)
                } else {
                  i0 = parseInt(inds[j])
                  i1 = parseInt(inds[j + 1])
                  i2 = parseInt(inds[j + 2])
                  indices.push(i0, i1, i2)
                }
              }
            }
          }
        } else if (inPointDataSection || inCellDataSection) {
          if (inColorSection) {
            // Get the colors

            while ((result = pat3Floats.exec(line)) !== null) {
              if (patWord.exec(line) !== null) break

              var r = parseFloat(result[1])
              var g = parseFloat(result[2])
              var b = parseFloat(result[3])
              colors.push(r, g, b)
            }
          } else if (inNormalsSection) {
            // Get the normal vectors

            while ((result = pat3Floats.exec(line)) !== null) {
              if (patWord.exec(line) !== null) break

              var nx = parseFloat(result[1])
              var ny = parseFloat(result[2])
              var nz = parseFloat(result[3])
              normals.push(nx, ny, nz)
            }
          }
        }

        if (patPOLYGONS.exec(line) !== null) {
          inPolygonsSection = true
          inPointsSection = false
          inTriangleStripSection = false
        } else if (patPOINTS.exec(line) !== null) {
          inPolygonsSection = false
          inPointsSection = true
          inTriangleStripSection = false
        } else if (patTRIANGLE_STRIPS.exec(line) !== null) {
          inPolygonsSection = false
          inPointsSection = false
          inTriangleStripSection = true
        } else if (patPOINT_DATA.exec(line) !== null) {
          inPointDataSection = true
          inPointsSection = false
          inPolygonsSection = false
          inTriangleStripSection = false
        } else if (patCELL_DATA.exec(line) !== null) {
          inCellDataSection = true
          inPointsSection = false
          inPolygonsSection = false
          inTriangleStripSection = false
        } else if (patCOLOR_SCALARS.exec(line) !== null) {
          inColorSection = true
          inNormalsSection = false
          inPointsSection = false
          inPolygonsSection = false
          inTriangleStripSection = false
        } else if (patNORMALS.exec(line) !== null) {
          inNormalsSection = true
          inColorSection = false
          inPointsSection = false
          inPolygonsSection = false
          inTriangleStripSection = false
        }
      }

      var geometry = new BufferGeometry()
      geometry.setIndex(indices)
      geometry.setAttribute('position', new Float32BufferAttribute(positions, 3))

      if (normals.length === positions.length) {
        geometry.setAttribute('normal', new Float32BufferAttribute(normals, 3))
      }

      if (colors.length !== indices.length) {
        // stagger

        if (colors.length === positions.length) {
          geometry.setAttribute('color', new Float32BufferAttribute(colors, 3))
        }
      } else {
        // cell

        geometry = geometry.toNonIndexed()
        var numTriangles = geometry.attributes.position.count / 3

        if (colors.length === numTriangles * 3) {
          var newColors = []

          for (var i = 0; i < numTriangles; i++) {
            var r = colors[3 * i + 0]
            var g = colors[3 * i + 1]
            var b = colors[3 * i + 2]

            newColors.push(r, g, b)
            newColors.push(r, g, b)
            newColors.push(r, g, b)
          }

          geometry.setAttribute('color', new Float32BufferAttribute(newColors, 3))
        }
      }

      return geometry
    }

    function parseBinary(data) {
      var count, pointIndex, i, numberOfPoints, s
      var buffer = new Uint8Array(data)
      var dataView = new DataView(data)

      // Points and normals, by default, are empty
      var points = []
      var normals = []
      var indices = []

      // Going to make a big array of strings
      var vtk = []
      var index = 0

      function findString(buffer, start) {
        var index = start
        var c = buffer[index]
        var s = []
        while (c !== 10) {
          s.push(String.fromCharCode(c))
          index++
          c = buffer[index]
        }

        return { start: start, end: index, next: index + 1, parsedString: s.join('') }
      }

      var state, line

      while (true) {
        // Get a string
        state = findString(buffer, index)
        line = state.parsedString

        if (line.indexOf('DATASET') === 0) {
          var dataset = line.split(' ')[1]

          if (dataset !== 'POLYDATA') throw new Error('Unsupported DATASET type: ' + dataset)
        } else if (line.indexOf('POINTS') === 0) {
          vtk.push(line)
          // Add the points
          numberOfPoints = parseInt(line.split(' ')[1], 10)

          // Each point is 3 4-byte floats
          count = numberOfPoints * 4 * 3

          points = new Float32Array(numberOfPoints * 3)

          pointIndex = state.next
          for (i = 0; i < numberOfPoints; i++) {
            points[3 * i] = dataView.getFloat32(pointIndex, false)
            points[3 * i + 1] = dataView.getFloat32(pointIndex + 4, false)
            points[3 * i + 2] = dataView.getFloat32(pointIndex + 8, false)
            pointIndex = pointIndex + 12
          }

          // increment our next pointer
          state.next = state.next + count + 1
        } else if (line.indexOf('TRIANGLE_STRIPS') === 0) {
          var numberOfStrips = parseInt(line.split(' ')[1], 10)
          var size = parseInt(line.split(' ')[2], 10)
          // 4 byte integers
          count = size * 4

          indices = new Uint32Array(3 * size - 9 * numberOfStrips)
          var indicesIndex = 0

          pointIndex = state.next
          for (i = 0; i < numberOfStrips; i++) {
            // For each strip, read the first value, then record that many more points
            var indexCount = dataView.getInt32(pointIndex, false)
            var strip = []
            pointIndex += 4
            for (s = 0; s < indexCount; s++) {
              strip.push(dataView.getInt32(pointIndex, false))
              pointIndex += 4
            }

            // retrieves the n-2 triangles from the triangle strip
            for (var j = 0; j < indexCount - 2; j++) {
              if (j % 2) {
                indices[indicesIndex++] = strip[j]
                indices[indicesIndex++] = strip[j + 2]
                indices[indicesIndex++] = strip[j + 1]
              } else {
                indices[indicesIndex++] = strip[j]
                indices[indicesIndex++] = strip[j + 1]
                indices[indicesIndex++] = strip[j + 2]
              }
            }
          }

          // increment our next pointer
          state.next = state.next + count + 1
        } else if (line.indexOf('POLYGONS') === 0) {
          var numberOfStrips = parseInt(line.split(' ')[1], 10)
          var size = parseInt(line.split(' ')[2], 10)
          // 4 byte integers
          count = size * 4

          indices = new Uint32Array(3 * size - 9 * numberOfStrips)
          var indicesIndex = 0

          pointIndex = state.next
          for (i = 0; i < numberOfStrips; i++) {
            // For each strip, read the first value, then record that many more points
            var indexCount = dataView.getInt32(pointIndex, false)
            var strip = []
            pointIndex += 4
            for (s = 0; s < indexCount; s++) {
              strip.push(dataView.getInt32(pointIndex, false))
              pointIndex += 4
            }

            // divide the polygon in n-2 triangle
            for (var j = 1; j < indexCount - 1; j++) {
              indices[indicesIndex++] = strip[0]
              indices[indicesIndex++] = strip[j]
              indices[indicesIndex++] = strip[j + 1]
            }
          }

          // increment our next pointer
          state.next = state.next + count + 1
        } else if (line.indexOf('POINT_DATA') === 0) {
          numberOfPoints = parseInt(line.split(' ')[1], 10)

          // Grab the next line
          state = findString(buffer, state.next)

          // Now grab the binary data
          count = numberOfPoints * 4 * 3

          normals = new Float32Array(numberOfPoints * 3)
          pointIndex = state.next
          for (i = 0; i < numberOfPoints; i++) {
            normals[3 * i] = dataView.getFloat32(pointIndex, false)
            normals[3 * i + 1] = dataView.getFloat32(pointIndex + 4, false)
            normals[3 * i + 2] = dataView.getFloat32(pointIndex + 8, false)
            pointIndex += 12
          }

          // Increment past our data
          state.next = state.next + count
        }

        // Increment index
        index = state.next

        if (index >= buffer.byteLength) {
          break
        }
      }

      var geometry = new BufferGeometry()
      geometry.setIndex(new BufferAttribute(indices, 1))
      geometry.setAttribute('position', new BufferAttribute(points, 3))

      if (normals.length === points.length) {
        geometry.setAttribute('normal', new BufferAttribute(normals, 3))
      }

      return geometry
    }

    function Float32Concat(first, second) {
      const firstLength = first.length,
        result = new Float32Array(firstLength + second.length)

      result.set(first)
      result.set(second, firstLength)

      return result
    }

    function Int32Concat(first, second) {
      var firstLength = first.length,
        result = new Int32Array(firstLength + second.length)

      result.set(first)
      result.set(second, firstLength)

      return result
    }

    function parseXML(stringFile) {
      // Changes XML to JSON, based on https://davidwalsh.name/convert-xml-json

      function xmlToJson(xml) {
        // Create the return object
        var obj = {}

        if (xml.nodeType === 1) {
          // element

          // do attributes

          if (xml.attributes) {
            if (xml.attributes.length > 0) {
              obj['attributes'] = {}

              for (var j = 0; j < xml.attributes.length; j++) {
                var attribute = xml.attributes.item(j)
                obj['attributes'][attribute.nodeName] = attribute.nodeValue.trim()
              }
            }
          }
        } else if (xml.nodeType === 3) {
          // text

          obj = xml.nodeValue.trim()
        }

        // do children
        if (xml.hasChildNodes()) {
          for (var i = 0; i < xml.childNodes.length; i++) {
            var item = xml.childNodes.item(i)
            var nodeName = item.nodeName

            if (typeof obj[nodeName] === 'undefined') {
              var tmp = xmlToJson(item)

              if (tmp !== '') obj[nodeName] = tmp
            } else {
              if (typeof obj[nodeName].push === 'undefined') {
                var old = obj[nodeName]
                obj[nodeName] = [old]
              }

              var tmp = xmlToJson(item)

              if (tmp !== '') obj[nodeName].push(tmp)
            }
          }
        }

        return obj
      }

      // Taken from Base64-js
      function Base64toByteArray(b64) {
        var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
        var i
        var lookup = []
        var revLookup = []
        var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
        var len = code.length

        for (i = 0; i < len; i++) {
          lookup[i] = code[i]
        }

        for (i = 0; i < len; ++i) {
          revLookup[code.charCodeAt(i)] = i
        }

        revLookup['-'.charCodeAt(0)] = 62
        revLookup['_'.charCodeAt(0)] = 63

        var j, l, tmp, placeHolders, arr
        var len = b64.length

        if (len % 4 > 0) {
          throw new Error('Invalid string. Length must be a multiple of 4')
        }

        placeHolders = b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0
        arr = new Arr((len * 3) / 4 - placeHolders)
        l = placeHolders > 0 ? len - 4 : len

        var L = 0

        for (i = 0, j = 0; i < l; i += 4, j += 3) {
          tmp =
            (revLookup[b64.charCodeAt(i)] << 18) |
            (revLookup[b64.charCodeAt(i + 1)] << 12) |
            (revLookup[b64.charCodeAt(i + 2)] << 6) |
            revLookup[b64.charCodeAt(i + 3)]
          arr[L++] = (tmp & 0xff0000) >> 16
          arr[L++] = (tmp & 0xff00) >> 8
          arr[L++] = tmp & 0xff
        }

        if (placeHolders === 2) {
          tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4)
          arr[L++] = tmp & 0xff
        } else if (placeHolders === 1) {
          tmp =
            (revLookup[b64.charCodeAt(i)] << 10) |
            (revLookup[b64.charCodeAt(i + 1)] << 4) |
            (revLookup[b64.charCodeAt(i + 2)] >> 2)
          arr[L++] = (tmp >> 8) & 0xff
          arr[L++] = tmp & 0xff
        }

        return arr
      }

      function parseDataArray(ele, compressed) {
        var numBytes = 0

        if (json.attributes.header_type === 'UInt64') {
          numBytes = 8
        } else if (json.attributes.header_type === 'UInt32') {
          numBytes = 4
        }

        // Check the format
        if (ele.attributes.format === 'binary' && compressed) {
          var rawData, content, byteData, blocks, cSizeStart, headerSize, padding, dataOffsets, currentOffset

          if (ele.attributes.type === 'Float32') {
            var txt = new Float32Array()
          } else if (ele.attributes.type === 'Int64') {
            var txt = new Int32Array()
          }

          // VTP data with the header has the following structure:
          // [#blocks][#u-size][#p-size][#c-size-1][#c-size-2]...[#c-size-#blocks][DATA]
          //
          // Each token is an integer value whose type is specified by "header_type" at the top of the file (UInt32 if no type specified). The token meanings are:
          // [#blocks] = Number of blocks
          // [#u-size] = Block size before compression
          // [#p-size] = Size of last partial block (zero if it not needed)
          // [#c-size-i] = Size in bytes of block i after compression
          //
          // The [DATA] portion stores contiguously every block appended together. The offset from the beginning of the data section to the beginning of a block is
          // computed by summing the compressed block sizes from preceding blocks according to the header.

          rawData = ele['#text']

          byteData = Base64toByteArray(rawData)

          blocks = byteData[0]
          for (var i = 1; i < numBytes - 1; i++) {
            blocks = blocks | (byteData[i] << (i * numBytes))
          }

          headerSize = (blocks + 3) * numBytes
          padding = headerSize % 3 > 0 ? 3 - (headerSize % 3) : 0
          headerSize = headerSize + padding

          dataOffsets = []
          currentOffset = headerSize
          dataOffsets.push(currentOffset)

          // Get the blocks sizes after the compression.
          // There are three blocks before c-size-i, so we skip 3*numBytes
          cSizeStart = 3 * numBytes

          for (var i = 0; i < blocks; i++) {
            var currentBlockSize = byteData[i * numBytes + cSizeStart]

            for (var j = 1; j < numBytes - 1; j++) {
              // Each data point consists of 8 bytes regardless of the header type
              currentBlockSize = currentBlockSize | (byteData[i * numBytes + cSizeStart + j] << (j * 8))
            }

            currentOffset = currentOffset + currentBlockSize
            dataOffsets.push(currentOffset)
          }

          for (var i = 0; i < dataOffsets.length - 1; i++) {
            var data = unzlibSync(byteData.slice(dataOffsets[i], dataOffsets[i + 1]))
            content = data.buffer

            if (ele.attributes.type === 'Float32') {
              content = new Float32Array(content)
              txt = Float32Concat(txt, content)
            } else if (ele.attributes.type === 'Int64') {
              content = new Int32Array(content)
              txt = Int32Concat(txt, content)
            }
          }

          delete ele['#text']

          if (ele.attributes.type === 'Int64') {
            if (ele.attributes.format === 'binary') {
              txt = txt.filter(function (el, idx) {
                if (idx % 2 !== 1) return true
              })
            }
          }
        } else {
          if (ele.attributes.format === 'binary' && !compressed) {
            var content = Base64toByteArray(ele['#text'])

            //  VTP data for the uncompressed case has the following structure:
            // [#bytes][DATA]
            // where "[#bytes]" is an integer value specifying the number of bytes in the block of data following it.
            content = content.slice(numBytes).buffer
          } else {
            if (ele['#text']) {
              var content = ele['#text'].split(/\s+/).filter(function (el) {
                if (el !== '') return el
              })
            } else {
              var content = new Int32Array(0).buffer
            }
          }

          delete ele['#text']

          // Get the content and optimize it
          if (ele.attributes.type === 'Float32') {
            var txt = new Float32Array(content)
          } else if (ele.attributes.type === 'Int32') {
            var txt = new Int32Array(content)
          } else if (ele.attributes.type === 'Int64') {
            var txt = new Int32Array(content)

            if (ele.attributes.format === 'binary') {
              txt = txt.filter(function (el, idx) {
                if (idx % 2 !== 1) return true
              })
            }
          }
        } // endif ( ele.attributes.format === 'binary' && compressed )

        return txt
      }

      // Main part
      // Get Dom
      var dom = null

      if (window.DOMParser) {
        try {
          dom = new DOMParser().parseFromString(stringFile, 'text/xml')
        } catch (e) {
          dom = null
        }
      } else if (window.ActiveXObject) {
        try {
          dom = new ActiveXObject('Microsoft.XMLDOM')
          dom.async = false

          if (!(dom.loadXML(/* xml */))) {
            throw new Error(dom.parseError.reason + dom.parseError.srcText)
          }
        } catch (e) {
          dom = null
        }
      } else {
        throw new Error('Cannot parse xml string!')
      }

      // Get the doc
      var doc = dom.documentElement
      // Convert to json
      var json = xmlToJson(doc)
      var points = []
      var normals = []
      var indices = []

      if (json.PolyData) {
        var piece = json.PolyData.Piece
        var compressed = json.attributes.hasOwnProperty('compressor')

        // Can be optimized
        // Loop through the sections
        var sections = ['PointData', 'Points', 'Strips', 'Polys'] // +['CellData', 'Verts', 'Lines'];
        var sectionIndex = 0,
          numberOfSections = sections.length

        while (sectionIndex < numberOfSections) {
          var section = piece[sections[sectionIndex]]

          // If it has a DataArray in it

          if (section && section.DataArray) {
            // Depending on the number of DataArrays

            if (Object.prototype.toString.call(section.DataArray) === '[object Array]') {
              var arr = section.DataArray
            } else {
              var arr = [section.DataArray]
            }

            var dataArrayIndex = 0,
              numberOfDataArrays = arr.length

            while (dataArrayIndex < numberOfDataArrays) {
              // Parse the DataArray
              if ('#text' in arr[dataArrayIndex] && arr[dataArrayIndex]['#text'].length > 0) {
                arr[dataArrayIndex].text = parseDataArray(arr[dataArrayIndex], compressed)
              }

              dataArrayIndex++
            }

            switch (sections[sectionIndex]) {
              // if iti is point data
              case 'PointData':
                var numberOfPoints = parseInt(piece.attributes.NumberOfPoints)
                var normalsName = section.attributes.Normals

                if (numberOfPoints > 0) {
                  for (var i = 0, len = arr.length; i < len; i++) {
                    if (normalsName === arr[i].attributes.Name) {
                      var components = arr[i].attributes.NumberOfComponents
                      normals = new Float32Array(numberOfPoints * components)
                      normals.set(arr[i].text, 0)
                    }
                  }
                }

                break

              // if it is points
              case 'Points':
                var numberOfPoints = parseInt(piece.attributes.NumberOfPoints)

                if (numberOfPoints > 0) {
                  var components = section.DataArray.attributes.NumberOfComponents
                  points = new Float32Array(numberOfPoints * components)
                  points.set(section.DataArray.text, 0)
                }

                break

              // if it is strips
              case 'Strips':
                var numberOfStrips = parseInt(piece.attributes.NumberOfStrips)

                if (numberOfStrips > 0) {
                  var connectivity = new Int32Array(section.DataArray[0].text.length)
                  var offset = new Int32Array(section.DataArray[1].text.length)
                  connectivity.set(section.DataArray[0].text, 0)
                  offset.set(section.DataArray[1].text, 0)

                  var size = numberOfStrips + connectivity.length
                  indices = new Uint32Array(3 * size - 9 * numberOfStrips)

                  var indicesIndex = 0

                  for (var i = 0, len = numberOfStrips; i < len; i++) {
                    var strip = []

                    for (var s = 0, len1 = offset[i], len0 = 0; s < len1 - len0; s++) {
                      strip.push(connectivity[s])

                      if (i > 0) len0 = offset[i - 1]
                    }

                    for (var j = 0, len1 = offset[i], len0 = 0; j < len1 - len0 - 2; j++) {
                      if (j % 2) {
                        indices[indicesIndex++] = strip[j]
                        indices[indicesIndex++] = strip[j + 2]
                        indices[indicesIndex++] = strip[j + 1]
                      } else {
                        indices[indicesIndex++] = strip[j]
                        indices[indicesIndex++] = strip[j + 1]
                        indices[indicesIndex++] = strip[j + 2]
                      }

                      if (i > 0) len0 = offset[i - 1]
                    }
                  }
                }

                break

              // if it is polys
              case 'Polys':
                var numberOfPolys = parseInt(piece.attributes.NumberOfPolys)

                if (numberOfPolys > 0) {
                  var connectivity = new Int32Array(section.DataArray[0].text.length)
                  var offset = new Int32Array(section.DataArray[1].text.length)
                  connectivity.set(section.DataArray[0].text, 0)
                  offset.set(section.DataArray[1].text, 0)

                  var size = numberOfPolys + connectivity.length
                  indices = new Uint32Array(3 * size - 9 * numberOfPolys)
                  var indicesIndex = 0,
                    connectivityIndex = 0
                  var i = 0,
                    len = numberOfPolys,
                    len0 = 0

                  while (i < len) {
                    var poly = []
                    var s = 0,
                      len1 = offset[i]

                    while (s < len1 - len0) {
                      poly.push(connectivity[connectivityIndex++])
                      s++
                    }

                    var j = 1

                    while (j < len1 - len0 - 1) {
                      indices[indicesIndex++] = poly[0]
                      indices[indicesIndex++] = poly[j]
                      indices[indicesIndex++] = poly[j + 1]
                      j++
                    }

                    i++
                    len0 = offset[i - 1]
                  }
                }

                break

              default:
                break
            }
          }

          sectionIndex++
        }

        var geometry = new BufferGeometry()
        geometry.setIndex(new BufferAttribute(indices, 1))
        geometry.setAttribute('position', new BufferAttribute(points, 3))

        if (normals.length === points.length) {
          geometry.setAttribute('normal', new BufferAttribute(normals, 3))
        }

        return geometry
      } else {
        throw new Error('Unsupported DATASET type')
      }
    }

    // get the 5 first lines of the files to check if there is the key word binary
    var meta = decodeText(new Uint8Array(data, 0, 250)).split('\n')

    if (meta[0].indexOf('xml') !== -1) {
      return parseXML(decodeText(data))
    } else if (meta[2].includes('ASCII')) {
      return parseASCII(decodeText(data))
    } else {
      return parseBinary(data)
    }
  }
}

export { VTKLoader }
